/*
 * MIT License
 *
 * Copyright (c) 2023-present, tangli
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package tony.redis.service

import com.fasterxml.jackson.core.type.TypeReference
import com.fasterxml.jackson.databind.JavaType
import java.time.Duration
import java.util.concurrent.TimeUnit
import java.util.function.Supplier
import tony.core.utils.toCollectionJavaType
import tony.redis.valueOp

public sealed interface RedisValueOp :
    RedisValueGetOp,
    RedisValueSetOp {
    /**
     * get the value of key if value not exists use [supplier] to set and return.
     * @param T
     * @param key must not be null.
     * @param type the type will transform to.
     * @return null when key does not exist or used in pipeline / transaction.
     */
    public fun <T : Any> getOrSetCallback(
        key: String,
        type: Class<T>,
        supplier: Supplier<T>,
    ): T =
        valueOp
            .get(key)
            .outputTransformTo(type)
            .let {
                if (it != null) {
                    it
                } else {
                    val supply = supplier.get()
                    set(key, supply)
                    supply
                }
            }

    /**
     * get the value of key if value not exists use [value] to set and return.
     * @param T
     * @param key must not be null.
     * @param type the type will transform to.
     * @return null when key does not exist or used in pipeline / transaction.
     */
    public fun <T : Any> getOrSet(
        key: String,
        type: Class<T>,
        value: T,
    ): T =
        valueOp
            .get(key)
            .outputTransformTo(type)
            .let {
                if (it != null) {
                    it
                } else {
                    set(key, value)
                    value
                }
            }

    /**
     * get the value of key if value not exists use [supplier] to set and return.
     * @param T
     * @param key must not be null.
     * @param type the type will transform to.
     * @param timeout
     * @param timeUnit
     * @return null when key does not exist or used in pipeline / transaction.
     */
    public fun <T : Any> getOrSetCallback(
        key: String,
        type: Class<T>,
        timeout: Long,
        timeUnit: TimeUnit,
        supplier: Supplier<T>,
    ): T =
        valueOp
            .get(key)
            .outputTransformTo(type)
            .let {
                if (it != null) {
                    it
                } else {
                    val supply = supplier.get()
                    set(key, supply, timeout, timeUnit)
                    supply
                }
            }

    /**
     * get the value of key if value not exists use [value] to set and return.
     * @param T
     * @param key must not be null.
     * @param type the type will transform to.
     * @param timeout
     * @param timeUnit
     * @return null when key does not exist or used in pipeline / transaction.
     */
    public fun <T : Any> getOrSet(
        key: String,
        type: Class<T>,
        timeout: Long,
        timeUnit: TimeUnit,
        value: T,
    ): T =
        valueOp
            .get(key)
            .outputTransformTo(type)
            .let {
                if (it != null) {
                    it
                } else {
                    set(key, value, timeout, timeUnit)
                    value
                }
            }

    /**
     * get the value of key if value not exists use [supplier] to set and return.
     * @param T
     * @param key must not be null.
     * @param type the type will transform to.
     * @param timeout
     * @return null when key does not exist or used in pipeline / transaction.
     */
    public fun <T : Any> getOrSetCallback(
        key: String,
        type: Class<T>,
        timeout: Duration,
        supplier: Supplier<T>,
    ): T =
        valueOp
            .get(key)
            .outputTransformTo(type)
            .let {
                if (it != null) {
                    it
                } else {
                    val supply = supplier.get()
                    set(key, supply, timeout)
                    supply
                }
            }

    /**
     * get the value of key if value not exists use [value] to set and return.
     * @param T
     * @param key must not be null.
     * @param type the type will transform to.
     * @param timeout
     * @return null when key does not exist or used in pipeline / transaction.
     */
    public fun <T : Any> getOrSet(
        key: String,
        type: Class<T>,
        timeout: Duration,
        value: T,
    ): T =
        valueOp
            .get(key)
            .outputTransformTo(type)
            .let {
                if (it != null) {
                    it
                } else {
                    set(key, value, timeout)
                    value
                }
            }

    /**
     * @param type 兼容jackson.
     * @see [RedisValueOp.getOrSet]
     */
    public fun <T : Any> getOrSet(
        key: String,
        type: JavaType,
        supplier: Supplier<T>,
    ): T =
        valueOp
            .get(key)
            .outputTransformTo<T>(type)
            .let {
                if (it != null) {
                    it
                } else {
                    val supply = supplier.get()
                    set(key, supply)
                    supply
                }
            }

    /**
     * @param type 兼容jackson.
     * @see [RedisValueOp.getOrSet]
     */
    public fun <T : Any> getOrSet(
        key: String,
        type: JavaType,
        value: T,
    ): T =
        valueOp
            .get(key)
            .outputTransformTo<T>(type)
            .let {
                if (it != null) {
                    it
                } else {
                    set(key, value)
                    value
                }
            }

    /**
     * @param type 兼容jackson.
     * @see [RedisValueOp.getOrSet]
     */
    public fun <T : Any> getOrSetCallback(
        key: String,
        type: JavaType,
        timeout: Long = 0,
        timeUnit: TimeUnit,
        supplier: Supplier<T>,
    ): T =
        valueOp
            .get(key)
            .outputTransformTo<T>(type)
            .let {
                if (it != null) {
                    it
                } else {
                    val supply = supplier.get()
                    set(key, supply, timeout, timeUnit)
                    supply
                }
            }

    /**
     * @param type 兼容jackson.
     * @see [RedisValueOp.getOrSet]
     */
    public fun <T : Any> getOrSet(
        key: String,
        type: JavaType,
        timeout: Long = 0,
        timeUnit: TimeUnit,
        value: T,
    ): T =
        valueOp
            .get(key)
            .outputTransformTo<T>(type)
            .let {
                if (it != null) {
                    it
                } else {
                    set(key, value, timeout, timeUnit)
                    value
                }
            }

    /**
     * @param type 兼容jackson.
     * @see [RedisValueOp.getOrSet]
     */
    public fun <T : Any> getOrSetCallback(
        key: String,
        type: JavaType,
        timeout: Duration,
        supplier: Supplier<T>,
    ): T =
        valueOp
            .get(key)
            .outputTransformTo<T>(type)
            .let {
                if (it != null) {
                    it
                } else {
                    val supply = supplier.get()
                    set(key, supply, timeout)
                    supply
                }
            }

    /**
     * @param type 兼容jackson.
     * @see [RedisValueOp.getOrSet]
     */
    public fun <T : Any> getOrSet(
        key: String,
        type: JavaType,
        timeout: Duration,
        value: T,
    ): T =
        valueOp
            .get(key)
            .outputTransformTo<T>(type)
            .let {
                if (it != null) {
                    it
                } else {
                    set(key, value, timeout)
                    value
                }
            }

    /**
     * @param type 兼容jackson.
     * @see [RedisValueOp.getOrSet]
     */
    public fun <T : Any> getOrSetCallback(
        key: String,
        type: TypeReference<T>,
        supplier: Supplier<T>,
    ): T =
        valueOp
            .get(key)
            .outputTransformTo(type)
            .let {
                if (it != null) {
                    it
                } else {
                    val supply = supplier.get()
                    set(key, supply)
                    supply
                }
            }

    /**
     * @param type 兼容jackson.
     * @see [RedisValueOp.getOrSet]
     */
    public fun <T : Any> getOrSet(
        key: String,
        type: TypeReference<T>,
        value: T,
    ): T =
        valueOp
            .get(key)
            .outputTransformTo(type)
            .let {
                if (it != null) {
                    it
                } else {
                    set(key, value)
                    value
                }
            }

    /**
     * @param type 兼容jackson.
     * @see [RedisValueOp.getOrSet]
     */
    public fun <T : Any> getOrSetCallback(
        key: String,
        type: TypeReference<T>,
        timeout: Long,
        timeUnit: TimeUnit,
        supplier: Supplier<T>,
    ): T =
        valueOp
            .get(key)
            .outputTransformTo(type)
            .let {
                if (it != null) {
                    it
                } else {
                    val supply = supplier.get()
                    set(key, supply, timeout, timeUnit)
                    supply
                }
            }

    /**
     * @param type 兼容jackson.
     * @see [RedisValueOp.getOrSet]
     */
    public fun <T : Any> getOrSet(
        key: String,
        type: TypeReference<T>,
        timeout: Long,
        timeUnit: TimeUnit,
        value: T,
    ): T =
        valueOp
            .get(key)
            .outputTransformTo(type)
            .let {
                if (it != null) {
                    it
                } else {
                    set(key, value, timeout, timeUnit)
                    value
                }
            }

    /**
     * @param type 兼容jackson.
     * @see [RedisValueOp.getOrSet]
     */
    public fun <T : Any> getOrSetCallback(
        key: String,
        type: TypeReference<T>,
        timeout: Duration,
        supplier: Supplier<T>,
    ): T =
        valueOp
            .get(key)
            .outputTransformTo(type)
            .let {
                if (it != null) {
                    it
                } else {
                    val supply = supplier.get()
                    set(key, supply, timeout)
                    supply
                }
            }

    /**
     * @param type 兼容jackson.
     * @see [RedisValueOp.getOrSet]
     */
    public fun <T : Any> getOrSet(
        key: String,
        type: TypeReference<T>,
        timeout: Duration,
        value: T,
    ): T =
        valueOp
            .get(key)
            .outputTransformTo(type)
            .let {
                if (it != null) {
                    it
                } else {
                    set(key, value, timeout)
                    value
                }
            }
}

/**
 * redis value 保存操作
 * @author tangli
 * @date 2023/06/06 19:01
 */
public sealed interface RedisValueSetOp : RedisValueTransformer {
    /**
     * Set the value.
     *
     * @param T
     * @param key must not be null.
     * @param value must not be null.
     */
    public fun <T : Any> set(
        key: String,
        value: T,
    ): Unit =
        valueOp.set(key, value.inputTransformTo())

    /**
     * Set the value and expiration timeout for key.
     *
     * @param T
     * @param key must not be null.
     * @param value must not be null.
     * @param timeout
     * @param timeUnit
     */
    public fun <T : Any> set(
        key: String,
        value: T,
        timeout: Long,
        timeUnit: TimeUnit,
    ): Unit =
        if (timeout == 0L) {
            valueOp.set(key, value.inputTransformTo())
        } else {
            valueOp.set(key, value.inputTransformTo(), timeout, timeUnit)
        }

    /**
     * Set the value and expiration timeout for key.
     *
     * @param T
     * @param key must not be null.
     * @param value must not be null.
     * @param timeout must not be null.
     */
    public fun <T : Any> set(
        key: String,
        value: T,
        timeout: Duration,
    ): Unit =
        valueOp.set(key, value.inputTransformTo(), timeout)

    /**
     * 当键 **不存在** 时保存。
     *
     * @param T
     * @param key must not be null.
     * @param value
     * @param timeout must not be null.
     * @param timeUnit must not be null.
     * @return
     */
    public fun <T : Any> setIfAbsent(
        key: String,
        value: T,
        timeout: Long,
        timeUnit: TimeUnit,
    ): Boolean? =
        if (timeout == 0L) {
            valueOp.setIfAbsent(key, value.inputTransformTo())
        } else {
            valueOp.setIfAbsent(key, value.inputTransformTo(), timeout, timeUnit)
        }

    /**
     * 当键 **不存在** 时保存。
     *
     * @param T
     * @param key must not be null.
     * @param value must not be null.
     * @param timeout must not be null.
     * @return
     */
    public fun <T : Any> setIfAbsent(
        key: String,
        value: T,
        timeout: Duration,
    ): Boolean? =
        valueOp.setIfAbsent(key, value.inputTransformTo(), timeout)

    /**
     * 当键 **存在** 时保存。
     *
     * @param T
     * @param key must not be null.
     * @param value must not be null.
     * @param timeout the key expiration timeout.
     * @param timeUnit must not be null.
     * @return
     */
    public fun <T : Any> setIfPresent(
        key: String,
        value: T,
        timeout: Long,
        timeUnit: TimeUnit,
    ): Boolean? =
        if (timeout == 0L) {
            valueOp.setIfPresent(key, value.inputTransformTo())
        } else {
            valueOp.setIfPresent(key, value.inputTransformTo(), timeout, timeUnit)
        }

    /**
     * 当键 **存在** 时保存。
     *
     * @param T
     * @param key must not be null.
     * @param value must not be null.
     * @param timeout must not be null.
     * @return
     */
    public fun <T : Any> setIfPresent(
        key: String,
        value: T,
        timeout: Duration,
    ): Boolean? =
        valueOp.setIfPresent(key, value.inputTransformTo(), timeout)

    /**
     * Set multiple keys to multiple values using key-value pairs provided in {@code tuple}.
     *
     * @param keyValues must not be null.
     * @see <a href="https://redis.io/commands/mset">Redis Documentation: MSET</a>
     */
    public fun multiSet(keyValues: Map<String, Any>): Unit =
        valueOp.multiSet(keyValues)

    /**
     * Set multiple keys to multiple values using key-value pairs provided in tuple only if the provided key does not exist.
     *
     * @param keyValues must not be null.
     * @return null when used in pipeline / transaction.
     * @see <a href="https://redis.io/commands/mset">Redis Documentation: MSET</a>
     */
    public fun multiSetIfAbsent(keyValues: Map<String, Any>): Boolean? =
        valueOp.multiSetIfAbsent(keyValues)

    /**
     * Set the value and expiration timeout for key.
     * Return the old string stored at key, or null if key did not exist.
     * An error is returned and SET aborted if the value stored at key is not a string.
     *
     * @param T
     * @param key must not be null.
     * @param value
     * @param timeout must not be null.
     * @param timeUnit must not be null.
     * @return
     */
    public fun <T : Any> setGet(
        key: String,
        value: T,
        type: Class<T>,
        timeout: Long,
        timeUnit: TimeUnit,
    ): T? =
        valueOp.setGet(key, value.inputTransformTo(), timeout, timeUnit).outputTransformTo(type)

    /**
     * @param type component type
     * @see [setGet]
     */
    public fun <T : Any> setGet(
        key: String,
        value: T,
        type: JavaType,
        timeout: Long,
        timeUnit: TimeUnit,
    ): T? =
        valueOp.setGet(key, value.inputTransformTo(), timeout, timeUnit).outputTransformTo(type)

    /**
     * @param type component type
     * @see [setGet]
     */
    public fun <T : Any> setGet(
        key: String,
        value: T,
        type: TypeReference<T>,
        timeout: Long,
        timeUnit: TimeUnit,
    ): T? =
        valueOp.setGet(key, value.inputTransformTo(), timeout, timeUnit).outputTransformTo(type)

    /**
     * Set the value and expiration timeout for key.
     * Return the old string stored at key, or null if key did not exist.
     * An error is returned and SET aborted if the value stored at key is not a string.
     *
     * @param T
     * @param key must not be null.
     * @param value
     * @param timeout must not be null.
     * @return
     */
    public fun <T : Any> setGet(
        key: String,
        value: T,
        type: Class<T>,
        timeout: Duration,
    ): T? =
        valueOp.setGet(key, value.inputTransformTo(), timeout).outputTransformTo(type)

    /**
     * @param type component type
     * @see [setGet]
     */
    public fun <T : Any> setGet(
        key: String,
        value: T,
        type: JavaType,
        timeout: Duration,
    ): T? =
        valueOp.setGet(key, value.inputTransformTo(), timeout).outputTransformTo(type)

    /**
     * @param type component type
     * @see [setGet]
     */
    public fun <T : Any> setGet(
        key: String,
        value: T,
        type: TypeReference<T>,
        timeout: Duration,
    ): T? =
        valueOp.setGet(key, value.inputTransformTo(), timeout).outputTransformTo(type)
}

/**
 * redis value 读取操作
 *
 * @author tangli
 * @date 2023/06/06 19:01
 */
public sealed interface RedisValueGetOp : RedisValueTransformer {
    /**
     * Get the value of key.
     *
     * @param T
     * @param key must not be null.
     * @param type the type will transform to.
     * @return null when key does not exist or used in pipeline / transaction.
     */
    public fun <T : Any> get(
        key: String,
        type: Class<T>,
    ): T? =
        valueOp
            .get(key)
            .outputTransformTo(type)

    /**
     * @param type 兼容jackson.
     * @see [RedisValueGetOp.get]
     */
    public fun <T : Any> get(
        key: String,
        type: JavaType,
    ): T? =
        valueOp
            .get(key)
            .outputTransformTo(type)

    /**
     * @param type 兼容jackson
     * @see [RedisValueGetOp.get]
     */
    public fun <T : Any> get(
        key: String,
        type: TypeReference<T>,
    ): T? =
        valueOp
            .get(key)
            .outputTransformTo(type)

    /**
     * Set {@code value} of {@code key} and return its old value.
     *
     * @param key must not be {@literal null}.
     * @return {@literal null} when key does not exist or used in pipeline / transaction.
     * @see <a href="https://redis.io/commands/getset">Redis Documentation: GETSET</a>
     */
    public fun <T : Any> getAndSet(
        key: String,
        type: Class<T>,
        value: T,
    ): T? =
        valueOp.getAndSet(key, value.inputTransformTo()).outputTransformTo(type)

    /**
     * Set {@code value} of {@code key} and return its old value.
     *
     * @param key must not be {@literal null}.
     * @return {@literal null} when key does not exist or used in pipeline / transaction.
     * @see <a href="https://redis.io/commands/getset">Redis Documentation: GETSET</a>
     */
    public fun <T : Any> getAndSet(
        key: String,
        type: JavaType,
        value: T,
    ): T? =
        valueOp.getAndSet(key, value.inputTransformTo()).outputTransformTo(type)

    /**
     * Set {@code value} of {@code key} and return its old value.
     *
     * @param key must not be {@literal null}.
     * @return {@literal null} when key does not exist or used in pipeline / transaction.
     * @see <a href="https://redis.io/commands/getset">Redis Documentation: GETSET</a>
     */
    public fun <T : Any> getAndSet(
        key: String,
        type: TypeReference<T>,
        value: T,
    ): T? =
        valueOp.getAndSet(key, value.inputTransformTo()).outputTransformTo(type)

    /**
     * Get multiple [keys]. Values are in the order of the requested keys Absent field values are represented using
     * null in the resulting [List].
     *
     * @param keys must not be null.
     * @return null when used in pipeline / transaction.
     * @see <a href="https://redis.io/commands/mget">Redis Documentation: MGET</a>
     */
    public fun multiGet(keys: Collection<String>): List<*> =
        valueOp
            .multiGet(keys) ?: listOf<Any>()

    /**
     * @param type component type
     * @see [multiGet]
     */
    public fun <T> multiGet(
        keys: Collection<String>,
        type: Class<T>,
    ): List<T> =
        valueOp
            .multiGet(keys)
            .outputTransformTo(type.toCollectionJavaType(List::class.java))
            ?: listOf()

    /**
     * @param type component type
     * @see [multiGet]
     */
    public fun <T> multiGet(
        keys: Collection<String>,
        type: JavaType,
    ): List<T> =
        valueOp
            .multiGet(keys)
            .outputTransformTo(type.toCollectionJavaType(List::class.java))
            ?: listOf()

    /**
     * @param type component type
     * @see [multiGet]
     */
    public fun <T> multiGet(
        keys: Collection<String>,
        type: TypeReference<T>,
    ): List<T> =
        valueOp
            .multiGet(keys)
            .outputTransformTo(type.type.toCollectionJavaType(List::class.java))
            ?: listOf()

    /**
     * Set value of key and return its old value.
     * @param T
     * @param key must not be null.
     * @param type the type will transform to.
     * @return null when key does not exist or used in pipeline / transaction.
     */
    public fun <T : Any> getAndSet(
        key: String,
        value: T,
        type: Class<T>,
    ): T? =
        valueOp
            .getAndSet(key, value.inputTransformTo())
            .outputTransformTo(type)

    /**
     * @param type 兼容jackson.
     * @see [RedisValueGetOp.getAndSet]
     */
    public fun <T : Any> getAndSet(
        key: String,
        value: T,
        type: JavaType,
    ): T? =
        valueOp
            .getAndSet(key, value.inputTransformTo())
            .outputTransformTo(type)

    /**
     * @param type 兼容jackson.
     * @see [RedisValueGetOp.getAndSet]
     */
    public fun <T : Any> getAndSet(
        key: String,
        value: T,
        type: TypeReference<T>,
    ): T? =
        valueOp
            .getAndSet(key, value.inputTransformTo())
            .outputTransformTo(type)

    /**
     * Return the value at key and delete the key.
     *
     * @param T
     * @param key must not be null.
     * @param type the type will transform to.
     * @return null when key does not exist or used in pipeline / transaction.
     */
    public fun <T : Any> getAndDelete(
        key: String,
        type: Class<T>,
    ): T? =
        valueOp
            .getAndDelete(key)
            .outputTransformTo(type)

    /**
     * @param type 兼容jackson.
     * @see [RedisValueGetOp.getAndDelete]
     */
    public fun <T : Any> getAndDelete(
        key: String,
        type: JavaType,
    ): T? =
        valueOp
            .getAndDelete(key)
            .outputTransformTo(type)

    /**
     * @param type 兼容jackson.
     * @see [RedisValueGetOp.getAndDelete]
     */
    public fun <T : Any> getAndDelete(
        key: String,
        type: TypeReference<T>,
    ): T? =
        valueOp
            .getAndDelete(key)
            .outputTransformTo(type)

    /**
     * Return the value at key and expire the key by applying timeout.
     * @param T
     * @param key must not be null.
     * @param type the type will transform to.
     * @param timeout
     * @param timeUnit must not be null.
     * @return
     */
    public fun <T : Any> getAndExpire(
        key: String,
        type: Class<T>,
        timeout: Long,
        timeUnit: TimeUnit,
    ): T? =
        valueOp
            .getAndExpire(key, timeout, timeUnit)
            .outputTransformTo(type)

    /**
     * @param type 兼容jackson.
     * @see [RedisValueGetOp.getAndExpire]
     */
    public fun <T : Any> getAndExpire(
        key: String,
        type: JavaType,
        timeout: Long,
        timeUnit: TimeUnit,
    ): T? =
        valueOp
            .getAndExpire(key, timeout, timeUnit)
            .outputTransformTo(type)

    /**
     * @param type 兼容jackson.
     * @see [RedisValueGetOp.getAndExpire]
     */
    public fun <T : Any> getAndExpire(
        key: String,
        type: TypeReference<T>,
        timeout: Long,
        timeUnit: TimeUnit,
    ): T? =
        valueOp
            .getAndExpire(key, timeout, timeUnit)
            .outputTransformTo(type)

    /**
     * Return the value at key and expire the key by applying timeout.
     * @param T
     * @param key must not be null.
     * @param type the type will transform to.
     * @param timeout must not be null.
     * @return
     */
    public fun <T : Any> getAndExpire(
        key: String,
        type: Class<T>,
        timeout: Duration,
    ): T? =
        valueOp
            .getAndExpire(key, timeout)
            .outputTransformTo(type)

    /**
     * @param type 兼容jackson.
     * @see [RedisValueGetOp.getAndExpire]
     */
    public fun <T : Any> getAndExpire(
        key: String,
        type: JavaType,
        timeout: Duration,
    ): T? =
        valueOp
            .getAndExpire(key, timeout)
            .outputTransformTo(type)

    /**
     * @param type 兼容jackson.
     * @see [RedisValueGetOp.getAndExpire]
     */
    public fun <T : Any> getAndExpire(
        key: String,
        type: TypeReference<T>,
        timeout: Duration,
    ): T? =
        valueOp
            .getAndExpire(key, timeout)
            .outputTransformTo(type)

    /**
     * Return the value at key and persist the key. This operation removes any TTL that is associated with key.
     * @param T
     * @param key must not be null.
     * @param type the type will transform to.
     * @return
     */
    public fun <T : Any> getAndPersist(
        key: String,
        type: Class<T>,
    ): T? =
        valueOp
            .getAndPersist(key)
            .outputTransformTo(type)

    /**
     * @param type 兼容jackson.
     * @see [RedisValueGetOp.getAndPersist]
     */
    public fun <T : Any> getAndPersist(
        key: String,
        type: JavaType,
    ): T? =
        valueOp
            .getAndPersist(key)
            .outputTransformTo(type)

    /**
     * @param type 兼容jackson.
     * @see [RedisValueGetOp.getAndPersist]
     */
    public fun <T : Any> getAndPersist(
        key: String,
        type: TypeReference<T>,
    ): T? =
        valueOp
            .getAndPersist(key)
            .outputTransformTo(type)
}
